#ifndef SWE_PROFILING_H
#define SWE_PROFILING_H

#include "common.h"
#if SWEGL_LINUX
#	include <sys/time.h>
#endif

namespace Profiling
{

class Zone
{
public:
	Zone(const char *name, Zone *parent, Zone *category);

	void reset();

	const char *name() const;
	uint numCalls() const;

	double totalTimeInSec() const;
	double avgTimeInSec() const;
	double minTimeInSec() const;
	double maxTimeInSec() const;

	uint64 totalTime() const;
	uint64 avgTime() const;
	uint64 minTime() const;
	uint64 maxTime() const;

//private:
	const char *m_name;
	Zone *m_parent;
	Zone *m_category;
	uint m_numCalls;
	uint64 m_totalTime;
	uint64 m_minTime;
	uint64 m_maxTime;

};

const int MAX_PROFILING_ZONES = 512;

class Server
{
public:
	static Server Instance;
	int m_numZones;
	Zone *m_zones[MAX_PROFILING_ZONES];
	uint64 m_ticksPerSecond;
	double m_ticksPerSecondAsDouble;

	void reset();

private:
	Server();
};

class ScopedSection
{
public:
	ScopedSection(Zone &zone);
	~ScopedSection();

	Zone &m_zone;
	uint64 m_startTime;

private:
	ScopedSection &operator = (const ScopedSection &rhs);
};


inline Zone::Zone(const char *name, Zone *parent, Zone *category) :
	m_name(name), m_parent(parent), m_category(category),
	m_numCalls(0), m_totalTime(0), m_minTime((uint64) -1), m_maxTime(0)
{
	sweglAssert(Server::Instance.m_numZones < MAX_PROFILING_ZONES);
	Server::Instance.m_zones[Server::Instance.m_numZones++] = this;
}

inline const char *Zone::name() const
{
	return m_name;
}

inline uint Zone::numCalls() const
{
	return m_numCalls;
}

inline double Zone::totalTimeInSec() const
{
	return (double) m_totalTime / Profiling::Server::Instance.m_ticksPerSecondAsDouble;
}

inline double Zone::avgTimeInSec() const
{
	return (double) m_totalTime / Profiling::Server::Instance.m_ticksPerSecondAsDouble / m_numCalls;
}

inline double Zone::minTimeInSec() const
{
	return (double) m_minTime / Profiling::Server::Instance.m_ticksPerSecondAsDouble;
}

inline double Zone::maxTimeInSec() const
{
	return (double) m_maxTime / Profiling::Server::Instance.m_ticksPerSecondAsDouble;
}

inline uint64 Zone::totalTime() const
{
	return m_totalTime * 1000000000 / Profiling::Server::Instance.m_ticksPerSecond;
}

inline uint64 Zone::avgTime() const
{
	return m_totalTime * 1000000000 / Profiling::Server::Instance.m_ticksPerSecond / m_numCalls;
}

inline uint64 Zone::minTime() const
{
	return m_minTime * 1000000000 / Profiling::Server::Instance.m_ticksPerSecond;
}

inline uint64 Zone::maxTime() const
{
	return m_maxTime * 1000000000 / Profiling::Server::Instance.m_ticksPerSecond;
}

#if SWEGL_WINDOWS
inline uint64 getPerformanceCounter()
{
	uint64 t;
	QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&t));
	return t;
}
#elif SWEGL_LINUX
inline uint64 getPerformanceCounter()
{
	struct timeval t;
	gettimeofday(&t, 0);
	return static_cast<uint64>(t.tv_usec);
}
#endif

inline ScopedSection::ScopedSection(Zone &zone) : m_zone(zone)
{
	m_zone.m_numCalls++;
	m_startTime = getPerformanceCounter();
}

inline ScopedSection::~ScopedSection()
{
	uint64 t = getPerformanceCounter() - m_startTime;
	m_zone.m_totalTime += t;
	if (m_zone.m_minTime > t) m_zone.m_minTime = t;
	if (m_zone.m_maxTime < t) m_zone.m_maxTime = t;
}

}	

#define DefineProfilingZone(name) static Profiling::Zone ProfilingZone__ ## name(#name, 0, 0)
#define ProfileZone(name) Profiling::ScopedSection scope_##__LINE__(ProfilingZone__ ## name)
#define ProfileFunction()  static Profiling::Zone ProfilingZone__ ## __FUNCSIG__(__FUNCTION__, 0, 0); Profiling::ScopedSection scope_##__LINE__(ProfilingZone__ ## __FUNCSIG__)
#define ProfileBlock(name) static Profiling::Zone ProfilingZone__ ## __LINE__(#name, 0, 0); Profiling::ScopedSection scope_##__LINE__(ProfilingZone__ ## __LINE__)

#endif

